﻿using System;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using System.Collections.Generic;

namespace Scalextric
{
	public partial class F1Lights : UserControl
	{
		private StatusType _Status = StatusType.Default;
		private Lighting _Lighting = Lighting.Solid;
		private bool _RaceStarting = false;

		private double _TimeDelay = 0;

		public StatusType Status { get { return _Status; } set {/* do nothing*/ } }
		public Lighting Lighting { get { return _Lighting; } set { /* do nothing*/} }
		public double TimeDelay { get { return _TimeDelay; } set { _TimeDelay = value; } }
		public bool RaceStarting { get { return _RaceStarting; } set { _RaceStarting = value; } }
		public int FlashesPerSecond { get { return flashCountMax / 2; } set { flashCountMax = (value * 2); } }

		private DispatcherTimer timer = new DispatcherTimer();
		private DispatcherTimer flashTimer = new DispatcherTimer();
		private int flashCount = 0;
		private int flashCountMax = 4;
		private List<Image> flashingBulbList = new List<Image>();

		public event StatusChangedHandler StatusChanged;
		public event StartRaceHandler RaceStarted;

		public F1Lights()
		{
			InitializeComponent();
		}

		public void SetStatus(StatusType newStatus)
		{
			//set controls status to new value
			_Status = newStatus;
			//update control
			UpdateControlStatusChanged();
			//call event
			NotifyStatusChange();
		}

		public void SetStatus(StatusType newStatus, double _Delay) //for race start ???
		{
			//set controls status to new value
			_Status = newStatus;
			TimeDelay = _Delay;
			//update control
			UpdateControlStatusChanged();
			//call event
			NotifyStatusChange();
			
		}

		private void NotifyStatusChange()
		{
			if (StatusChanged != null)
			{
				StatusArgs sa = new StatusArgs(_Status);
				StatusChanged(this, sa);
			}
		}

		private void NotifyRaceStart()
		{
			if (RaceStarted != null)
			{
				RaceStarted(this, EventArgs.Empty);
			}
		}

		private void StartFlash()
		{
			//check timer running.
			if (!flashTimer.IsEnabled)
			{
				flashTimer.Interval = new TimeSpan(0, 0, 0, 0, (1000/flashCountMax));//2 times a sec
				flashTimer.Tick += new EventHandler(flashTimer_Tick);
				flashTimer.Start();
			}
			// check which tick its on
		}

		private void StopFlash()
		{
			if (flashTimer.IsEnabled)
			{
				flashTimer.Stop();
				flashTimer.Tick -= new EventHandler(flashTimer_Tick);
				flashCount = 0;
				//need to reset any lights back to original
				foreach (Image img in flashingBulbList)
				{
					if (img.Tag.GetType() == typeof(BitmapImage))
						img.Source = Tag as BitmapImage;
				}
			}
		}

		void flashTimer_Tick(object sender, EventArgs e)
		{
			//add one to count
			flashCount++;
			//change lights to alternative
			//use flashing list and check all bulbs
			//original coulor On odds
			// bool isEven = (i % 2 == 0); i=int
			foreach (Image img in flashingBulbList)
			{
				//check tag
				if (img.Tag != null)
				{
					if (flashCount % 2 == 0)
					{
						//even
						//make grey
						UpdateBulbColour(img, LightColour.Grey);
					}
					else
					{
						//odd
						//return to original image (stored in tag)
						img.Source = img.Tag as BitmapImage;
					}
				}
			}
			//update count
			if (flashCount == flashCountMax)
				flashCount = 0; //reset count to next second
		}



		private void StartRace()
		{
			_RaceStarting = true;
			//mkae sure within 4-7 secs
			if (_TimeDelay < 4)
				_TimeDelay = 4;
			else if (_TimeDelay > 7)
				_TimeDelay = 7;

			UpdateAllBulbs(LightColour.Grey, Lighting.Solid);
			int a = 0;
			timer.Interval = new TimeSpan(0, 0, 0, 1);
			timer.Tick += delegate(object sender, EventArgs e)
			{
				switch (a)
				{
					case 0:
						UpdateLight(LightBulb.Red1, LightColour.Red, Lighting.Solid);
						break;
					case 1:
						UpdateLight(LightBulb.Red2, LightColour.Red, Lighting.Solid);
						break;
					case 2:
						UpdateLight(LightBulb.Red3, LightColour.Red, Lighting.Solid);
						break;
					case 3:
						UpdateLight(LightBulb.Red4, LightColour.Red, Lighting.Solid);
						break;
					case 4:
						timer.Interval = new TimeSpan(0, 0, 0, 0, (int)(_TimeDelay *1000)); //change rnadom number
						UpdateLight(LightBulb.Red5, LightColour.Red, Lighting.Solid);
						break;
					case 5:
						UpdateAllBulbs(LightColour.Grey, Lighting.Solid);
						UpdateBulbSet("Green", LightColour.Green, Lighting.Solid);
						timer.Stop();
						//call race started event
						NotifyRaceStart();
						//race starting finished
						RaceStarting = false;
						break;
					default:
						
						return;
				}
				a++;
			};
			timer.Start();
		}

		private void UpdateLight(LightBulb lb, LightColour col, Lighting ltype)
		{
			//update a bulb to do what it needs
			//get bulb
			Image _selImage = null;
			switch (lb)
			{
				case LightBulb.Orange1:
					_selImage = Orange1;   
					break;
				case LightBulb.Orange2:
					_selImage = Orange2;  
					break;
				case LightBulb.Orange3:
					_selImage = Orange3;  
					break;
				case LightBulb.Green1:
					_selImage = Green1;  
					break;
				case LightBulb.Green2:
					_selImage = Green2;  
					break;
				case LightBulb.Red1:
					_selImage = Red1; 
					break;
				case LightBulb.Red2:
					_selImage = Red2; 
					break;
				case LightBulb.Red3:
					_selImage = Red3; 
					break;
				case LightBulb.Red4:
					_selImage = Red4; 
					break;
				case LightBulb.Red5:
					_selImage = Red5; 
					break;
				default:
					return; //no light found to exit now!
			}

			//change colour
			UpdateBulbColour(_selImage, col);

			//change to solid / flashing
			UpdateLightingType(_selImage, ltype);

			//refresh ???
			this.UpdateLayout();
		}

		private void UpdateAllBulbs(LightColour colour, Lighting lighting) //updates all bulbs
		{
			foreach (LightBulb value in Enum.GetValues(typeof(LightBulb)))
			{
				string val = value.ToString();
				Image imgObj = (Image)this.FindName(val);
				//update image
				UpdateBulbColour(imgObj, colour);
				UpdateLightingType(imgObj, lighting);
			}
		}

		private void UpdateBulbSet(string BulbSet, LightColour colour, Lighting lighting) //updates all red bulbs
		{
			foreach (LightBulb value in Enum.GetValues(typeof(LightBulb)))
			{
				string val = value.ToString();
				if (val.StartsWith(BulbSet))
				{
					Image imgObj = (Image)this.FindName(val);
					UpdateBulbColour(imgObj, colour);
					UpdateLightingType(imgObj, lighting);
				}
			}
		}

		private void UpdateBulbColour(Image bulb, LightColour colour)
		{
			switch (colour)
			{
				case LightColour.Red:
					bulb.Source = this.Resources["redLight"] as BitmapImage;
					break;
				case LightColour.Orange:
					bulb.Source = this.Resources["orangeLight"] as BitmapImage;
					break;
				case LightColour.Green:
					bulb.Source = this.Resources["greenLight"] as BitmapImage;
					break;
				case LightColour.Grey:
					bulb.Source = this.Resources["greyLight"] as BitmapImage;
					break;
				default:
					bulb.Source = this.Resources["greyLight"] as BitmapImage;
					break;
			}
			//Refresh(bulb);
			bulb.UpdateLayout();
		}

		private void UpdateLightingType(Image bulb, Lighting ltype)
		{
			switch (ltype)
			{
				case Lighting.Solid:
					//keep solid
					//remove from flist
					if (flashingBulbList.Contains(bulb))
						flashingBulbList.Remove(bulb);
					break;
				case Lighting.Flashing:
					StartFlash();
					//change to flashing animation/storyboard - between grey and coloured version - 05.sec each so 2 a sec. - have 2 animations? one qiucker - so 1/4 sec in stead of half?
					if (!flashingBulbList.Contains(bulb))
					{
						//set tag to original source
						bulb.Tag = (bulb.Source as BitmapImage);
						flashingBulbList.Add(bulb);
					}
					break;
				default:
					//keep solid
					break;
			}
			if (flashingBulbList.Count == 0)
				StopFlash();
		}

		private void UpdateControlStatusChanged()
		{
			switch(_Status)
			{
				case StatusType.Stop:
					UpdateBulbSet("Red", LightColour.Red, Lighting.Solid);
					UpdateBulbSet("Green", LightColour.Grey, Lighting.Solid);
					UpdateBulbSet("Orange", LightColour.Grey, Lighting.Solid);
					break;
				case StatusType.Go:
					UpdateBulbSet("Red", LightColour.Grey, Lighting.Solid);
					UpdateBulbSet("Green", LightColour.Green, Lighting.Solid);
					UpdateBulbSet("Orange", LightColour.Grey, Lighting.Solid);
					break;
				case StatusType.Warning:
					UpdateBulbSet("Red", LightColour.Grey, Lighting.Solid);
					UpdateBulbSet("Green", LightColour.Grey, Lighting.Solid);
					UpdateBulbSet("Orange", LightColour.Orange, Lighting.Flashing);
					break;
				case StatusType.StartingRace:
					StartRace();
					break;
				case StatusType.Default:
					UpdateAllBulbs(LightColour.Grey, Lighting.Solid);
					break;
				default:
					//do default anyway
					UpdateAllBulbs(LightColour.Grey, Lighting.Solid);
					break;
			}
		}
	}


	public delegate void StatusChangedHandler(object sender, StatusArgs e); //StatusArgs

	public delegate void StartRaceHandler(object sender, EventArgs e);

	public class StatusArgs : System.EventArgs
	{
		public StatusType NewStatus;

		public StatusArgs(StatusType status)
		{
			this.NewStatus = status;
		}
	}

	public enum StatusType
	{
		Go,
		Warning,
		Stop,
		StartingRace,
		Default
	}

	public enum Lighting
	{
		Solid,
		Flashing
	}

	public enum LightBulb
	{
		Orange1,
		Orange2,
		Orange3,
		Green1,
		Green2,
		Red1,
		Red2,
		Red3,
		Red4,
		Red5
	}

	public enum LightColour
	{
		Red,
		Orange,
		Green,
		Grey
	}
}
